// sm3_test.c: 实现SM3哈希算法并测试对"abcd"的哈希结果  
#include <stdio.h>  
#include <stdint.h>  
#include <string.h>  
#include <stdlib.h>  

// SM3 初始向量  
const uint32_t SM3_IV[8] = {  
    0x7380166F,  
    0x4914B2B9,  
    0x172442D7,  
    0xDA8A0600,  
    0xA96F30BC,  
    0x163138AA,  
    0xE38DEE4D,  
    0xB0FB0E4E  
};  

// SM3 上下文结构体  
typedef struct {  
    uint32_t total[2];    // 消息长度，以位为单位  
    uint32_t state[8];    // 哈希状态  
    unsigned char buffer[64]; // 数据缓冲区  
} sm3_context;  

// 大端序读取4字节为一个32位无符号整数  
#define GET_ULONG_BE(n,b,i)                             \
    do {                                                \
        (n) = ((uint32_t)(b)[(i)] << 24)             \
            | ((uint32_t)(b)[(i) + 1] << 16)         \
            | ((uint32_t)(b)[(i) + 2] << 8)          \
            | ((uint32_t)(b)[(i) + 3]);              \
    } while(0)  

// 大端序写入32位无符号整数为4字节  
#define PUT_ULONG_BE(n,b,i)                             \
    do {                                                \
        (b)[(i)]     = (unsigned char)((n) >> 24);    \
        (b)[(i) + 1] = (unsigned char)((n) >> 16);    \
        (b)[(i) + 2] = (unsigned char)((n) >> 8);     \
        (b)[(i) + 3] = (unsigned char)((n));          \
    } while(0)  

// SM3 循环左移  
#define ROTL(x,n) (((x) << (n)) | ((x) >> (32 - (n))))  

// 定义布尔函数  
#define FF0(x,y,z) ((x) ^ (y) ^ (z))  
#define FF1(x,y,z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))  
#define GG0(x,y,z) ((x) ^ (y) ^ (z))  
#define GG1(x,y,z) (((x) & (y)) | ((~(x)) & (z)))  

// 非线性变换函数  
#define P0(x) ((x) ^ ROTL((x),9) ^ ROTL((x),17))  
#define P1(x) ((x) ^ ROTL((x),15) ^ ROTL((x),23))  

// SM3 常量函数 Tj  
#define Tj(j) ((j) <= 15 ? 0x79CC4519 : 0x7A879D8A)  

// 填充常量  
static const unsigned char sm3_padding[64] = {  
    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 后续填充为0 */  
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ...重复...*/  
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ...共64个...*/  
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
    0x00, 0x00, 0x00, 0x00  
};  

// SM3 初始化  
void sm3_starts(sm3_context *ctx) {  
    ctx->total[0] = 0;  
    ctx->total[1] = 0;  
    memcpy(ctx->state, SM3_IV, sizeof(SM3_IV));  
}  

// SM3 处理一个64字节的数据块  
static void sm3_process(sm3_context *ctx, const unsigned char data[64]) {  
    uint32_t W[68], W1[64], A, B, C, D, E, F, G, H;  
    uint32_t SS1, SS2, TT1, TT2;  
    int j;  

    // 消息扩展  
    for (j = 0; j < 16; j++) {  
        GET_ULONG_BE(W[j], data, j * 4);  
    }  
    for (j = 16; j < 68; j++) {  
        W[j] = P1(W[j - 16] ^ W[j - 9] ^ ROTL(W[j - 3], 15)) ^ ROTL(W[j - 13], 7) ^ W[j - 6];  
    }  
    for (j = 0; j < 64; j++) {  
        W1[j] = W[j] ^ W[j + 4];  
    }  

    // 初始化寄存器  
    A = ctx->state[0];  
    B = ctx->state[1];  
    C = ctx->state[2];  
    D = ctx->state[3];  
    E = ctx->state[4];  
    F = ctx->state[5];  
    G = ctx->state[6];  
    H = ctx->state[7];  

    // 压缩函数  
    for (j = 0; j < 64; j++) {  
        SS1 = ROTL((ROTL(A, 12) + E + ROTL(Tj(j), j % 32)), 7);  
        SS2 = SS1 ^ ROTL(A, 12);  
        TT1 = (j <= 15 ? FF0(A, B, C) : FF1(A, B, C)) + D + SS2 + W1[j];  
        TT2 = (j <= 15 ? GG0(E, F, G) : GG1(E, F, G)) + H + SS1 + W[j];  
        D = C;  
        C = ROTL(B, 9);  
        B = A;  
        A = TT1;  
        H = G;  
        G = ROTL(F, 19);  
        F = E;  
        E = P0(TT2);  
    }  

    // 更新状态  
    ctx->state[0] ^= A;  
    ctx->state[1] ^= B;  
    ctx->state[2] ^= C;  
    ctx->state[3] ^= D;  
    ctx->state[4] ^= E;  
    ctx->state[5] ^= F;  
    ctx->state[6] ^= G;  
    ctx->state[7] ^= H;  
}  

// SM3 更新函数  
void sm3_update(sm3_context *ctx, const unsigned char *input, int ilen) {  
    int fill;  
    uint32_t left;  

    if (ilen <= 0)  
        return;  

    left = ctx->total[0] & 0x3F;  
    fill = 64 - left;  

    ctx->total[0] += ilen;  
    if (ctx->total[0] < (uint32_t)ilen)  
        ctx->total[1]++;  

    if (left && ilen >= fill) {  
        memcpy(ctx->buffer + left, input, fill);  
        sm3_process(ctx, ctx->buffer);  
        input += fill;  
        ilen -= fill;  
        left = 0;  
    }  

    while (ilen >= 64) {  
        sm3_process(ctx, input);  
        input += 64;  
        ilen -= 64;  
    }  

    if (ilen > 0) {  
        memcpy(ctx->buffer + left, input, ilen);  
    }  
}  

// SM3 完成并输出哈希值  
void sm3_finish(sm3_context *ctx, unsigned char output[32]) {  
    unsigned long high, low;  
    unsigned long last, padn;  
    unsigned char msglen[8];  

    high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);  
    low = (ctx->total[0] << 3);  

    PUT_ULONG_BE(high, msglen, 0);  
    PUT_ULONG_BE(low, msglen, 4);  

    last = ctx->total[0] & 0x3F;  
    padn = (last < 56) ? (56 - last) : (120 - last);  

    sm3_update(ctx, sm3_padding, padn);  
    sm3_update(ctx, msglen, 8);  

    PUT_ULONG_BE(ctx->state[0], output, 0);  
    PUT_ULONG_BE(ctx->state[1], output, 4);  
    PUT_ULONG_BE(ctx->state[2], output, 8);  
    PUT_ULONG_BE(ctx->state[3], output, 12);  
    PUT_ULONG_BE(ctx->state[4], output, 16);  
    PUT_ULONG_BE(ctx->state[5], output, 20);  
    PUT_ULONG_BE(ctx->state[6], output, 24);  
    PUT_ULONG_BE(ctx->state[7], output, 28);  
}  

// 单次调用 SM3 算法  
void sm3(const unsigned char *input, int ilen, unsigned char output[32]) {  
    sm3_context ctx;  

    sm3_starts(&ctx);  
    sm3_update(&ctx, input, ilen);  
    sm3_finish(&ctx, output);  

    memset(&ctx, 0, sizeof(sm3_context)); // 清零上下文  
}  

// 打印缓冲区为十六进制  
void dumpbuf(const unsigned char *buf, int len) {  
    for (int i = 0; i < len; i++) {  
        printf("%02x", buf[i]);  
    }  
    printf("\n");  
}  

// 主函数，用于测试对"abc"的 SM3 哈希  
int main(void) {  
    const unsigned char data[] = "abcd";  
    unsigned char hash[32];  

    printf("消息：%s\nHash结果：\n", data);  
    sm3(data, strlen((const char*)data), hash);  
    dumpbuf(hash, 32);  

    return 0;  
}
